package cn.snmp.mutual.uk.stack;

import java.io.*;
import java.util.*;
import cn.snmp.mutual.uk.util.SnmpUtilities;

public abstract class Pdu extends Observable {
    private static final String version_id = "@(#)$Id: Pdu.java,v 3.33 2008/12/12 14:55:51 tpanton Exp $ Copyright Westhawk Ltd";
    protected Vector reqVarbinds;
    protected Vector respVarbinds;
    private static final String TIMED_OUT = "Timed out";
    private static final String[] errorStrings;
    private static int next_id;
    private static final Object NEXT_ID_LOCK;
    private int[] retry_intervals;
    protected byte[] encodedPacket;
    protected SnmpContextBasisFace context;
    protected boolean added;
    protected byte msg_type;
    int req_id;
    protected Integer snmpv3MsgId;
    protected int errstat;
    protected int errind;
    private Transmitter trans;
    private int retries;
    protected boolean answered;
    private boolean got;
    private boolean isTimedOut;
    private PduException respException;

    protected void new_value(final int n, final Varbind res) {
    }

    protected void tell_them() {
        this.notifyObservers();
    }

    public Pdu(final SnmpContextBasisFace con) {
        this.reqVarbinds = null;
        this.respVarbinds = null;
        this.retry_intervals = new int[]{500, 1000, 2000, 5000, 5000};
        this.encodedPacket = null;
        this.added = false;
        this.snmpv3MsgId = null;
        this.trans = null;
        this.got = false;
        this.respException = null;
        this.context = con;
        synchronized (Pdu.NEXT_ID_LOCK) {
            this.req_id = Pdu.next_id++;
        }
        this.errstat = 0;
        this.errind = 0;
        this.reqVarbinds = new Vector(1, 1);
        this.setMsgType((byte) (-96));
        this.isTimedOut = false;
        this.answered = false;
    }

    public SnmpContextBasisFace getContext() {
        return this.context;
    }

    public void setRetryIntervals(final int[] rinterval) {
        this.retry_intervals = rinterval;
    }

    public int[] getRetryIntervals() {
        return this.retry_intervals;
    }

    public boolean send() throws IOException, PduException {
        return this.send(this.errstat, this.errind);
    }

    protected boolean send(final int error_status, final int error_index) throws IOException, PduException {
        if (!this.added) {
            this.added = this.context.addPdu(this);
        }
        final Enumeration vbs = this.reqVarbinds.elements();
        this.encodedPacket = this.context.encodePacket(this.msg_type, this.req_id, error_status, error_index, vbs, this.snmpv3MsgId);
        this.addToTrans();
        return this.added;
    }

    protected void addToTrans() {
        if (this.added && this.trans != null) {
            synchronized (this.trans) {
                this.trans.setPdu(this);
                this.trans.stand();
            }
        }
    }

    protected boolean sendme() {
        this.context.sendPacket(this.encodedPacket);
        ++this.retries;
        if (AsnObject.debug > 6) {
            System.out.println(this.getClass().getName() + ".sendme(): Sent Pdu reqId=" + this.req_id + ", retries " + this.retries);
        }
        return this.retries > 3;
    }

    @Deprecated
    public boolean send(final String com) throws IOException, PduException {
        if (this.context instanceof SnmpContext) {
            ((SnmpContext) this.context).setCommunity(com);
        }
        return this.send();
    }

    public void addOid(final String oid) {
        final Varbind vb = new Varbind(oid);
        this.addOid(vb);
    }

    public void addOid(final AsnObjectId oid) {
        final Varbind vb = new Varbind(oid);
        this.addOid(vb);
    }

    public void addOid(final String oid, final AsnObject val) {
        final Varbind vb = new Varbind(oid, val);
        this.addOid(vb);
    }

    public void addOid(final AsnObjectId oid, final AsnObject val) {
        final Varbind vb = new Varbind(oid, val);
        this.addOid(vb);
    }

    public void addOid(final Varbind var) {
        this.reqVarbinds.addElement(var);
    }

    public Varbind[] getRequestVarbinds() {
        final int sz = this.reqVarbinds.size();
        final Varbind[] arr = new Varbind[sz];
        this.reqVarbinds.copyInto(arr);
        return arr;
    }

    public Varbind[] getResponseVarbinds() throws PduException {
        if (this.respException != null) {
            throw this.respException;
        }
        Varbind[] arr = null;
        if (this.respVarbinds != null) {
            final int sz = this.respVarbinds.size();
            arr = new Varbind[sz];
            this.respVarbinds.copyInto(arr);
            if (sz == 1 && ((Varbind) this.respVarbinds.get(0)).getOid().equals(new AsnObjectId("1.3.6.1.6.3.15.1.1.3.0"))) {
                return null;
            }
        }
        return arr;
    }

    private void dump(final Vector v, final Varbind[] array) {
        final int sz = v.size();
        System.out.println("Vector: ");
        for (int i = 0; i < sz; ++i) {
            System.out.println("\t" + v.elementAt(i));
        }
        System.out.println("Array: ");
        for (int i = 0; i < array.length; ++i) {
            System.out.println("\t" + array[i]);
        }
        System.out.println("--");
    }

    void setResponseException(final PduException exc) {
        if (AsnObject.debug > 6) {
            System.out.println(this.getClass().getName() + ".setResponseException(): reqId=" + this.req_id + exc.getMessage());
        }
        this.respException = exc;
    }

    public int getReqId() {
        return this.req_id;
    }

    public int getErrorIndex() {
        return this.errind;
    }

    public int getErrorStatus() {
        return this.errstat;
    }

    public String getErrorStatusString() {
        String errString = "";
        if (this.errstat >= 0) {
            if (this.errstat < Pdu.errorStrings.length) {
                errString = Pdu.errorStrings[this.errstat];
                if (this.errstat == 5 && this.isTimedOut()) {
                    errString = "Timed out";
                }
            } else if (this.respException != null) {
                errString = this.respException.getMessage();
            } else {
                errString = "Decoding Exception";
            }
        }
        return errString;
    }

    public boolean isTimedOut() {
        return this.isTimedOut;
    }

    public boolean waitForSelf() {
        long del = 1000L;
        for (int i = 0; i < this.retry_intervals.length; ++i) {
            del += this.retry_intervals[i];
        }
        final boolean res = this.waitForSelf(del);
        if (AsnObject.debug > 6) {
            System.out.println(this.getClass().getName() + ".waitForSelf(): reqId=" + this.req_id + " " + res);
        }
        if (!this.answered) {
            this.handleNoAnswer();
        }
        return res;
    }

    @Override
    public String toString() {
        return this.toString(false);
    }

    protected String toString(final boolean withRespVars) {
        final StringBuffer buffer = new StringBuffer(this.getClass().getName());
        buffer.append("[");
        buffer.append("context=").append(this.context);
        buffer.append(", reqId=").append(this.req_id);
        buffer.append(", msgType=0x").append(SnmpUtilities.toHex(this.msg_type));
        buffer.append(", ");
        buffer.append(this.printVars("reqVarbinds", this.reqVarbinds));
        if (withRespVars) {
            buffer.append(", ");
            buffer.append(this.printVars("respVarbinds", this.respVarbinds));
        }
        buffer.append("]");
        return buffer.toString();
    }

    protected StringBuffer printVars(final String title, final Vector vars) {
        final StringBuffer buffer = new StringBuffer();
        buffer.append(title).append("=");
        if (vars != null) {
            final int sz = vars.size();
            buffer.append("[");
            for (int i = 0; i < sz; ++i) {
                if (i > 0) {
                    buffer.append(", ");
                }
                final Varbind var = (Varbind) vars.elementAt(i);
                buffer.append(var.toString());
            }
            buffer.append("]");
        } else {
            buffer.append("null");
        }
        return buffer;
    }

    synchronized boolean waitForSelf(final long delay) {
        if (!this.got) {
            try {
                this.wait(delay);
            } catch (InterruptedException ex) {
            }
        }
        return this.answered;
    }

    void transmit() {
        this.transmit(true);
    }

    void transmit(final boolean withRetries) {
        if (withRetries) {
            int n = 0;
            this.answered = false;
            while (!this.context.isDestroyed() && !this.answered && n < this.retry_intervals.length) {
                this.sendme();
                try {
                    Thread.sleep(this.retry_intervals[n]);
                } catch (InterruptedException ex) {
                }
                ++n;
            }
            if (!this.answered) {
                this.handleNoAnswer();
            }
        } else {
            this.sendme();
            this.answered = true;
        }
        if (!this.context.removePdu(this.req_id) && AsnObject.debug > 6) {
            System.out.println(this.getClass().getName() + ".transmit(): Failed to remove reqId " + this.req_id);
        }
    }

    void setTrans(final Transmitter t) {
        this.trans = t;
    }

    public byte getMsgType() {
        return this.msg_type;
    }

    protected void setMsgType(final byte type) {
        this.msg_type = type;
    }

    protected void setErrorStatus(final int err) {
        this.errstat = err;
        if (AsnObject.debug > 6) {
            System.out.println(this.getClass().getName() + ".setErrorStatus(): reqId=" + this.req_id + " " + this.errstat);
        }
        if (this.errstat != 0) {
            this.setResponseException(new AgentException(this.getErrorStatusString()));
        }
    }

    protected void setErrorStatus(final int err, final PduException exc) {
        this.errstat = err;
        this.setResponseException(exc);
    }

    protected void setErrorIndex(final int ind) {
        this.errind = ind;
    }

    protected boolean isExpectingResponse() {
        return true;
    }

    private void handleNoAnswer() {
        if (AsnObject.debug > 6) {
            System.out.println(this.getClass().getName() + ".handleNoAnswer(): reqId=" + this.req_id);
        }
        this.answered = true;
        this.isTimedOut = true;
        this.setErrorStatus(5);
        this.setErrorIndex(0);
        this.setChanged();
        this.tell_them();
        this.clearChanged();
        synchronized (this) {
            this.notify();
        }
    }

    void fillin(final AsnPduSequence seq) {
        if (this.answered) {
            if (AsnObject.debug > 6) {
                System.out.println(this.getClass().getName() + ".fillin(): Got a second answer to reqId " + this.req_id);
            }
            return;
        }
        if (seq != null) {
            if (seq.isCorrect) {
                int n = -1;
                try {
                    this.req_id = seq.getReqId();
                    this.setErrorStatus(seq.getWhatError());
                    this.setErrorIndex(seq.getWhereError());
                    final AsnSequence varBind = seq.getVarBind();
                    final int size = varBind.getObjCount();
                    this.respVarbinds = new Vector(size, 1);
                    for (n = 0; n < size; ++n) {
                        final Object obj = varBind.getObj(n);
                        if (obj instanceof AsnSequence) {
                            final AsnSequence varSeq = (AsnSequence) obj;
                            try {
                                final Varbind vb = new Varbind(varSeq);
                                this.respVarbinds.addElement(vb);
                                this.new_value(n, vb);
                            } catch (IllegalArgumentException ex) {
                            }
                        }
                    }
                    if (this.reqVarbinds.isEmpty()) {
                        this.reqVarbinds = (Vector) this.respVarbinds.clone();
                    }
                } catch (Exception e) {
                    final DecodingException exc = new DecodingException("Incorrect Varbind list, element " + n);
                    this.setErrorStatus(20, exc);
                }
            } else {
                final DecodingException exc2 = new DecodingException("Incorrect packet. No of bytes received less than packet length.");
                this.setErrorStatus(21, exc2);
            }
        }
        this.setChanged();
        this.tell_them();
        this.clearChanged();
        synchronized (this) {
            this.got = true;
            this.answered = true;
            this.notify();
            if (this.trans != null) {
                this.trans.interruptMe();
            }
        }
    }

    @Override
    public void notifyObservers(final Object arg) {
        if (this.respException != null) {
            super.notifyObservers(this.respException);
        } else {
            super.notifyObservers(arg);
        }
    }

    static {
        errorStrings = new String[]{"No error", "Value too big error", "No such name error", "Bad value error", "Read only error", "General error", "No access error", "Wrong type error", "Wrong length error", "Wrong encoding error", "Wrong value error", "No creation error", "Inconsistent value error", "Resource unavailable error", "Commit failed error", "Undo failed error", "Authorization error", "Not writable error", "Inconsistent name error"};
        Pdu.next_id = 1;
        NEXT_ID_LOCK = new Object();
    }
}
